program UK2025;

 // Version 2025                                                                                                                       26-Apr-2025
 //-----------------------------------------------------------------------------------------------------------------------------------------------
 // Written by: Paul Candler   email: paul.candler@hotmail.com
 // Based on original scripts written by Tim Scott for Fixed Course Tasks and AATs
 //
 // Scores a Fixed Course Task, Distance Handicapped Task, or AAT in accordance with "The 2025 Rules for BGA Rated Competitions"
 // If you have any problems with this script, or queries regarding using it with SeeYou, please email me at the above address 
 //
 // Notes:
 // 1.  Before importing into SeeYou Competition, check on BGA website for latest version of this script
 //     https://members.gliding.co.uk/library/information-for-organisers/seeyou-competition-scoring-script/
 //
 // 2.  Ensure 'CompClass' is set (in the table below) to the correct Competition Class.
 //     Remove the double slash from in front of the line containing the required Competition class and
 //     add a double slash in front of the line with no double slash (CompClass = '?') before importing
 //     into SeeYou Competition.
 //
const     // <<do not alter this line>>
          //#######################################################################################################################
            CompClass = '?',                            ClassVariables = '',
          //CompClass = 'Open Nationals',               ClassVariables = '1,50,100000,200000,40,150000,7200,1.18,250,200,0',
          //CompClass = '18m Nationals',                ClassVariables = '1,50,090000,180000,36,150000,7200,1.10,250,200,0',
          //CompClass = '15m Nationals',                ClassVariables = '1,50,090000,180000,36,150000,7200,1.04,250,200,0',
          //CompClass = 'Std Nationals',                ClassVariables = '1,50,080000,160000,32,150000,7200,1.00,250,200,0',
          //CompClass = '20m Two Seater Championship',  ClassVariables = '1,50,090000,180000,36,150000,7200,1.04,250,200,2',
          //CompClass = 'Club Nationals',               ClassVariables = '1,50,080000,160000,32,120000,7200,1.00,250,200,2',
          //CompClass = 'Junior Nationals',             ClassVariables = '0,40,060000,120000,30,080000,5400,1.00,000,000,1',
          //CompClass = 'Regionals',                    ClassVariables = '0,40,060000,120000,30,080000,5400,1.00,000,000,1',
          //CompClass = 'UK Simplified Rules',          ClassVariables = '2,00,000000,000000,00,080000,0000,1.00,000,000,1',
          //#######################################################################################################################

 // 3. Depending on the competition and task type, each day you may need to enter a combination of 'Contest Wind', 'Number of pilots withdrawn' and
 //    and 'Handicapped Task Distance' in SeeYou Competition in the 'Tag' box on the Contest Day Properties/General tab.
 //    Order is not specific, but use semi-colons as separators e.g. W=270/12;Nw=1 or Nw=1;H=310.6
 //
 //    Number of pilots withdrawn [BGA Rules, Table 7.2.1] is given by Nw=n, where n is the number of pilots that have withdrawn from
 //    a 1000 point competition. This information MUST be entered if any pilots withdraw from the contest, to ensure correct points calculations. 
 //
 //    Contest Wind is given by 'W=ddd/ss', where ddd is the wind direction and ss in the wind speed in knots, and is required for windicapped
 //    competitions. [BGA Rules, Rule 5.23.5], except for Distance Handicapped Tasks (DHT).
 //
 //    Handicapped Task Distance is given by H=nnn.n, and is required for DHTs, the windicapped distance in kilometres that
 //    the DHT task setting software has calculated for each glider. Entering the Handicapped Task Distance also triggers the script to score the task
 //    as a DHT. If this number is given as zero, then the distance is calculated as 'Declared task distance' * (100/max_HCap) where max_HCap is
 //    the maximum handicap of any glider not withdrawn from the competition. 
 //    
 // 4. On the Contest Day Properties/Task tab, ensure that Task type is set to 'Racing Task', and 'Task Time' is set to 00:00:00 for Fixed Course
 //    Tasks and DHTs. 
 //    For AATs, 'Task Type' must be 'Assigned Areas Task' and Task Time must be non-zero.
 //
 //******************************
 // Changes since version 2019
 //
 // 2021   Fixed error check for 'need leg data'
 //
 // 2022   Add UK Simplified Rules (for trial only)
 //        Display script version number with comp class
 //
 // 2023   Corrected rounding error for Day Speed/Distance points displayed in results header (info3 string)
 //        If handicap on/off flag in ClassVariables is set to '2' then use IGC handicaps
 //        Use IGC handicaps in Club Class
 //        Check handicaps given in any handicapped class are within sensible range [46-119.5 or 0.46-1.119.5]
 //        For 'Team Entry Different Gliders', if PilotTag set to "team" (not case sensitive) in Pilot Edit dialogue, then handicap used will be
 //        confirmed each day on scorer's screen
 //
 // 2023a  Corrected error in handling IGC handicaps in Club Class
 //        Initialize Info1 to Info4 strings to ensure null before use (fixes SeeYou 10.60 issue)
 // 2023c  Corrected windicap task distance calculation for establishing Y distance in Nationals
 //
 // 2025   Use IGC handicaps in 20m Class
 //        Increase max handicap to 119.5 (JS5)
 //        Minimum AAT Task Time reduced to 1.5 hours for Regionals and Junior Nationals
 //
 //-----------------------------------------------------------------------------------------------------------------------------------------------
 //conversion factors
  ms2kmh  = 3.6;
  deg2rad = Pi / 180;
 // Min and max handicaps listed in BGA 2025 Rulebook
  HCapMin = 46;
  HCapMax = 119.5;

 var
  // Class variable parameters set up in procedure SetUpCompParameters, held in string ClassVariables
  Qualifying_distance_factor,      // Percentage, used to calculate Y.                              {
  Ymin,                            // Minimum Qualifying distance in metres                         { See Rules Table 7.21
  Ymax,                            // Maximum Qualifying distance in metres                         {

  MinTaskDistance, MinTaskTime,    // Minimum task distance/time set at briefing in metres          {
  Qualifying_Time_Factor,          // Used in AATs to calculate Y                                   {
  Da, Ta :                         // Devaluation distance & Time adjustments.                      { See Rules Table 7.22
    integer;                       //                                                               {
  Contest_Wind_division_factor,    // Wind strength is divided by contest dependant factor.         {
  Wdiv :                           // Division constant used in wind increment calculation.         {
    double;

 // Numbers
  N,                      // Number of participating gliders
  Nl,                     // Number of participating gliders launched
  Ny,                     // Number of participating gliders reaching or exceeding Y
  Nv,                     // Number of participating gliders equalling or exceeding 2/3rds Vh
  Nf,                     // Number of participating gliders who complete the task and finish
  Ns,                     // Number of starts (including HC)
  NsComp,                 // Number of competing starts (used in Simplified Rules)
  Nw,                     // Number of pilots withdrawn
  TaskLegs,               // Number of legs in task (from Task data)
  LegDataCount,           // Number of traces with leg data
  ManualCount,            // Number of manually scored pilots (takeoff = 00:00:01)
  i :                     // Array index used in main procedure
  integer ;

 // Distance
  DTask,                  // Task distance as reported at briefing (not AATs)
  Hcap_task_dist,         // Handicapped Task Distance (actual distance in km that glider with handicap 100 must fly, given in DayTag string)
  Y,                      // Qualifying distance
  Dmax,                   // Greatest marking distance of any glider
  DmaxLand,	          // Greatest marking distance of any landout
  Dw,                     // Winners Marking Distance
  DSlotLength,            // Landout slot length for Simplified Rules (in metres)

 // Speed
  Vh,                     // Greatest handicapped speed

 // Handicapping
  W,                      // Contest Wind in knots
  Windkts,                // Actual wind strength in knots
  Winddeg,                // Direction of the Contest Wind in degrees relative to True North
  Radwind,                // Direction of the Contest Wind in radians relative to True North
  max_HCap,               // Highest handicap of competing gliders
  DHT_hc_factor,          // Multiplication factor to convert actual distance reported by SeeYou to handicapped distance
  Hbase,                  // 1 for IGC, 100 for BGA

 // Points
  F,                      // Day total points
  Fd,                     // Day distance points
  Fv,                     // Day speed points

 // Times
  Winners_Tg,             // Fixed Course Task winner's Tg (time to complete the course) in seconds
  Td,                     // Minimum Time for Assigned Area Tasks in seconds
  StartOpen,              // Start Open time for regatta starts, in seconds

 //Calculated Factors
  Ff,                     // Day points reduction Factor
  WindiCapTaskDist :      // Task distance adjusted for windicapping
  double;

 // Strings
  Devalued,               // string to hold reason for day devaluation
  debuginfo,              // string for debug info which may be output with ShowMessage(debuginfo)
  Hi_string,              // string to hold Hi values for optional output in Info2 or User_str1
  HStartString,           // string to hold Start Open time for held starts
  RStartString,           // string to hold Start Open time for regatta starts
  vnum :                  // version number
  string;

 // Booleans
  Nationals_Y,            //  if TRUE, y is %age of wind adjusted task distance, if FALSE y is %age of unhandicapped task distance
  handicapped_comp,       //  if TRUE, glider speed indices to be used, if FALSE use handicap of 100 for all gliders
  IGC_handicaps,          //  if TRUE use IGC handicaps (for Club Class)
  Fixed_Course,           //  determined at run-time, set TRUE if value of Task.Time=0
  Distance_Handicapped,   //  determined at run-time, set TRUE if 'H=nnn.n' given in Day Tag box
  Assigned_Area,          //  determined at run-time, set TRUE if value of Task.Time>0
  ThousandPoint,          //  determined at run-time, set FALSE if first character in ClassVariables (Nationals_Y) is a '2'
  RegattaStart,           //  determined at run-time, set TRUE if RS=nn:nn given in DayTag
  HeldStart,              //  determined at run-time, set TRUE if HS=nn:nn given in DayTag
  Manual_Scoring,         //  set TRUE if current pilot being manually scored
  Debug_Mode :            //  will be set TRUE if '?' precedes wind string in Day Tag box
  Boolean ;

//------------------------------------------------------------------------------------------------------------------------------------------------
// Functions and Procedures. (Main Program is at end of script)
//------------------------------------------------------------------------------------------------------------------------------------------------
// return the minimum value of a,b,c
function MinValue (a,b,c : double) : double;
var
  m : double;

begin
    m := a;
    If b < m then m := b;
    If c < m then m := c;
    Result := m;
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// return the maximum value of a,b
function MaxValue(a,b : double) : double;

begin
    If a > b then result := a else result := b;
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// use values in string ClassVariables to set the correct values for various parameters
// for each class, to ensure correct rules used in CalculateY and CalculateDaypoints etc.
procedure SetUpCompParameters;

begin
    vnum             := 'v25';
    handicapped_comp := false;
    IGC_handicaps    := false;
    Nationals_Y      := false;
    ThousandPoint    := true;

// ClassVariables = Nationals_Y,Qualifying_distance_factor,Ymin,Ymax,Qualifying_Time_Factor,MinTaskDistance,MinTaskTime,Contest_Wind_division_factor,Da,Ta,handicapped_comp

    Qualifying_distance_factor   := StrToInt  (copy(ClassVariables, 3,2),0);
    Ymin                         := StrToInt  (copy(ClassVariables, 6,6),0);
    Ymax                         := StrToInt  (copy(ClassVariables,13,6),0);
    Qualifying_Time_Factor       := StrToInt  (copy(ClassVariables,20,2),0);
    MinTaskDistance              := StrToInt  (copy(ClassVariables,23,6),0);
    MinTaskTime                  := StrToInt  (copy(ClassVariables,30,4),0);
    Contest_Wind_division_factor := StrToFloat(copy(ClassVariables,35,4)  );
    Da                           := StrToInt  (copy(ClassVariables,40,3),0);
    Ta                           := StrToInt  (copy(ClassVariables,44,3),0);

    Wdiv                         := 46.0;     // constant for all comps except Overseas Championships which are no longer supported

    If copy(ClassVariables, 1,1) = '1' then Nationals_Y      := true;
    If copy(ClassVariables, 1,1) = '2' then ThousandPoint    := false;             // Use Simplified Rules for scoring
    If not ThousandPoint then DslotLength := Ymin;                                 // For Simplified Rules, set slot length using Ymin field

    If (copy(ClassVariables,48,1) = '1') or (copy(ClassVariables,48,1) = '2') then handicapped_comp := true;
    If copy(ClassVariables,48,1) = '2' then IGC_handicaps := true;
    If IGC_Handicaps then Hbase := 1.0 else Hbase := 100.0;

    ManualCount := 0;

end;


//------------------------------------------------------------------------------------------------------------------------------------------------
// function to check for gliders with invalid handicap in Pilot Details and find max handicap
function CheckHcaps : boolean;

var
  x : integer;
  xh : double;

begin
    Result := false;
    max_HCap := 0;



    for x := low(Pilots) to high(Pilots) do
      begin
        Pilots[x].Warning :=  '';  // clear content of string from previous runs
        If (Pilots[x].Hcap > max_HCap)  then max_HCap :=  Pilots[x].Hcap;

        xh := Pilots[x].Hcap;

        // if BGA handicaps then expected range is HCapMin to HCapMax
        // if IGC handicaps then use same range by multiplying hcap by 100

        if IGC_handicaps then xh := xh * 100;
        If (xh < HCapMin) or (xh > HCapMax) then
          begin
            Result := true;
            Pilots[x].Warning :=  'Glider ' + Pilots[x].CompID + ' has invalid handicap = ' + FormatFloat('0.0',Pilots[x].Hcap) + ' !!?';
            debuginfo := Pilots[x].CompID + '  handicap = ' + FormatFloat('0.0',Pilots[x].Hcap);
            showmessage(debuginfo);
          end;
    end;
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate the number of participating gliders, N by stripping out HC and withdrawn pilots.
function GetNumberGliders : integer;

var
  x : integer;

begin
    for x := low(Pilots) to high(Pilots) do
    begin
      If PilotCompeting(Pilots[x]) then                      // ignore Hors Concours pilots.  
       Result := Result + 1;
    end;

    Result := Result - Nw;                                   // Reduce by number given as withdrawn in DayTag

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate the number of competing pilots launched, Nl 
function GetNumberPilotsLaunched : integer;

var
  x : integer;
begin
    for x := low(Pilots) to high(Pilots) do
    begin
      If PilotCompeting(Pilots[x]) then                      // ignore Hors Concours pilots
        If Pilots[x].Takeoff > 0 then Result := Result + 1;  // takeoff > 0 implies launched, withdrawn pilots will be DNF
    end;
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// Get task info and set global variables
procedure GetTaskInfo;

begin

    Fixed_Course  := false;
    Distance_Handicapped := false;
    Assigned_Area := false;

    If Task.TaskTime > 0 then Assigned_Area := true;
    If Task.TaskTime = 0 then Fixed_Course  := true;       // assume Fixed Course to start with, may be modified by content of DayTag string

    TaskLegs := GetArrayLength(Task.Point) - 1;

end;
//------------------------------------------------------------------------------------------------------------------------------------------------
// parse the Contest Day Tag string for wind info etc. and put results in global variables
procedure ParseDayTag(TagString : string);

var
    WindString, DHTString, WithdrawnString,
    taskdis,strength,direction,DHTdist,taskstr,windinfo : string;
    xoff : integer;

begin
    debug_mode := false;                                     // start with debug flag off
    Windkts    := 0;                                         // and zero wind parameters
    Winddeg    := 0;
    Radwind    := 0;
    windinfo := ' ';

    If Fixed_course then taskstr := 'Fixed Course Task' else taskstr := 'AAT';  // based on Task.Time being zero or non-zero

    If copy(TagString,1,1) = '?' then                       // If first character of TagString is question mark
      begin
        debug_mode := true;                                 // set flag for debug mode
        TagString := copy(TagString,2,99);                  // and strip question mark off
      end;

   TagString       := Uppercase(TagString);                  // convert any lowercase chars to uppercase

   WindString      := ExtractString(Tagstring,'W=');         // look for 'W=' and extract wind info
   DHTString       := ExtractString(TagString,'H=');         // look for 'H=' and extract Handicapped Task Distance
   WithdrawnString := ExtractString(TagString,'NW=');        // look for 'NW=' and extract number withdrawn
   RStartString    := ExtractString(TagString,'RS=');        // look for 'RS=' and extract start open time
   HStartString    := ExtractString(TagString,'HS=');        // look for 'HS=' and extract start open time

   RegattaStart := false;
   StartOpen := 0;                                          // Set Start Open time to 0
   xoff  := pos(':',RStartString);                          // check if start string is present and formatted correctly
   If xoff > 0 then                                         // extract hour and minute from string and convert to seconds
          StartOpen := (StrToInt(copy(RStartString,1,xoff-1),0)*3600) + (StrToInt(copy(RStartString,xoff+1,10),0)*60);

    // ** DO NOT allow Regatta starts in 1000 point scoring - will require additional mods to script for it to work properly
   If (not ThousandPoint) and (StartOpen > 0) then RegattaStart := true;

   HeldStart := false;
   If not RegattaStart then                                 // ignore HS= if RS= already found
      begin
        xoff  := pos(':',HStartString);                      // check if start string is present and formatted correctly
        If xoff > 0 then                                     // extract hour and minute from string and convert to seconds
          StartOpen := (StrToInt(copy(HStartString,1,xoff-1),0)*3600) + (StrToInt(copy(HStartString,xoff+1,10),0)*60);

    // ** DO NOT allow Held starts in 1000 point scoring - will require additional mods to script for it to work properly
        If (not ThousandPoint) and (StartOpen > 0) then HeldStart := true;
      end;

   If length(WithdrawnString) > 0 then
        Nw := StrToInt(WithdrawnString,0) else Nw := 0;      //  set number withdrawn

   If length(DHTString) > 0 then
      begin
        Distance_Handicapped := true;                        // set flag for Distance Handicapped Task, but leave Fixed_Course true
        handicapped_comp := false;                           // and switch off handicapping
                                                             // wind speed and direction will default to 0
        Hcap_task_dist := 0;
        Hcap_task_dist := StrToFloat(DHTString);             // get DHT task distance for handicap 100 in km, will remain 0.0 if no distance given
        taskstr := 'Distance Handicapped Task';
      end;

    xoff  := pos('/',WindString);                             // check if wind string is present and formatted correctly
    If xoff > 0 then
      begin
        direction := copy(WindString,1,xoff-1);               // extract the wind and direction from the wind string
        strength  := copy(WindString,xoff + 1,10);
        Windkts   := StrToInt(strength,0);                    // store results in global variables
        Winddeg   := StrToInt(direction,0);
        Radwind   := Winddeg * Deg2Rad;                       // convert degrees to radians

        W := Minvalue( (Windkts/Contest_Wind_division_factor), 30,30 );   // get value for W, must not exceed 30 (Rule 5.22.5)

     
        windinfo :=  ',   Contest Wind ' + FormatFloat('0',Winddeg) + ' degs/' + FormatFloat('0',Windkts) + ' kts';
      end;

      If (xoff = 0) and not Distance_Handicapped then windinfo := ', Wind not set or format incorrect';

      Info1 :=  CompClass + ' (' + vnum + ') / ' + taskstr + windinfo;


end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// convert seconds to string in format [h:]mm:ss
Function SecondsToTime (xsecs : double) : string;

var
xh,xm,xs : integer;
h0,m0,s0 : string;

begin

    Result := '';                                      // return null string if string not found
    h0 := '';
    m0 := '';
    s0 := '';

    xh := trunc(xsecs) div 3600;
    xm := (trunc(xsecs) - (xh*3600)) div 60;
    xs := trunc(xsecs) - (xh*3600) - (xm*60);

    if xh >  0 then h0 := inttostr(xh) + ':';         // unlikely to have hours, but cater for it
    if (xh >  0) and (xm < 10) then m0 := '0';        // need leading 0 on minutes if hours present
    if xs < 10 then s0 := '0';
    Result := h0 + m0 + inttostr(xm) + ':' + s0 + inttostr(xs);

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// find specified substring in xstring and return characters to right of '=' up to semi-colon or end of string
Function ExtractString (xstring,substring : string) : string;

var
  x : integer;

begin
      
    Result := '';                                      // return null string if string not found

    x := pos(substring,xstring);                       // locate string
    if x > 0 then
      begin
        Result :=  copy(xstring,x,99);                 // copy from start of string and everything to the right
        x := pos('=',Result);                          // locate '=' sign
        if x > 0 then Result :=  copy(Result,x+1,99);  // copy from right of '=' sign

        x := pos(';',Result);                          // check for trailing semi-colon
        if x > 0 then Result := copy(Result,1,x-1);    // strip off semi-colon and everything to its right
    end;

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate DTask
procedure SetDTask;

begin

    If Fixed_course then DTask := Task.TotalDis;                        // Get total task distance from Task record

    If Distance_Handicapped then
      begin
        If Hcap_task_dist = 0.0 then DHT_hc_factor := 100/Max_HCap else // no distance entered, use max handicap to calculate DHT_hc_factor
          DHT_hc_factor := (Hcap_task_dist * 1000)/Task.TotalDis;       // convert distance to metres and divide by full task distance

        DTask := Task.TotalDis * DHT_hc_factor;                         // Adjust task distance to representative handicapped task distance
      end;

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate the Hi value and store in Task.Point.Td1 for each leg of a Fixed Course Task
procedure CalculateTaskHiValues;

var
    x : integer;
  sep : string;

begin
    sep := ' ';
    Hi_string     := 'Leg Hi values =';                                          // initialize string for Hi values

    for x := 1 to TaskLegs do
      begin
        Task.Point[x].Td1 := CalcLegHi(Task.Point[x].Crs);                       // calculate the wind handicap increment for each leg
        Hi_string := Hi_string + sep + FormatFloat('0.000',Task.Point[x].Td1);   // save in Hi_string for possible output in debug mode
        sep := ',  ';
      end;

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// Calculate Windicapped task distance for given Hcap
Function CalculateWindicapTaskDist (Hcap : Double) : Double;

var
  x : integer;
  LegHl : double;

begin
    Result := 0;

    For x := 1 to TaskLegs do
    begin                                                                     // Task.Point[x].Td1 already holds Hi for that leg
      LegHl := CalcLegHl(Hcap,Task.Point[x].Td1);                             // get LegHl
      Task.Point[x].Td2 := (Task.Point[x].D * 100) / LegHl;                   // calculate the windicap leg distance and store in Task.Point.Td2
      Result := Result + Task.Point[x].Td2;                                   // add windicap leg distance to Result
    end;

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate and return the Leg Handicap Increment (Hi)
Function CalcLegHi (legcrs : Double) : Double;

var
  Theta,Wind : Double;

begin
    Wind  := W/Wdiv;                   // Apply Wdiv constant
    Theta := Legcrs - Radwind;         // Rules define Theta as "non-Reflexive angle between track and direction wind is coming from",
                                       // but difference between wind direction and course will always give desired result in following formula
                                       // regardless of whether or not reflexive or non-reflexive angle is used                                    

    Result :=  100 * (Power(1 - (power(Wind,2)*power(sin(Theta),2)),0.5) - (1 + (Wind)*cos(Theta)));

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate and return the Leg Wind Adjusted handicap (Hl)
Function CalcLegHl(hcap,Hi : Double) : Double;

var
  H : Double;

begin

    If not handicapped_comp then
      H := 100                                    // if not handicapped_comp all gliders will have handicap of 100
    else
      H := hcap;

    If IGC_handicaps then H := H * 100;           // if using IGC handicaps then multiply by 100 to get in correct range

    Result := Maxvalue(25,H + Hi);                // Return Hl, sum of handicap and leg handicap increment (windicap). Must be >=25

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// Check check to see if trace data present or manually scored
// *** Manual scoring will only work for fixed course tasks ***
procedure CheckPilotData(var Pilot : TPilot);


begin
    Pilot.User_str1 := '';
    Pilot.User_str2 := '';
    Manual_scoring := False;

    If getarraylength(Pilot.Leg) > 0 then                          // no leg data means no trace or possibly 'need task legs data' not set in contest properties
                              LegDataCount := LegDataCount + 1;    // count pilots with leg data in trace

    if round(Pilot.Takeoff) = 1 then                               // for manual scoring Takeoff must be set to 00:00:01
      begin
        Manual_scoring := True;
        ManualCount := ManualCount + 1;                            // Count number of manually scored
        Pilot.User_Str2 := ' <<manually scored>>';                 // flag in User_Str2
      end;


    // if there is takeoff > 00:00:01 and distance, but no leg data then either "Need task legs data ..." has not been set, or manual scoring attempted
    // if other traces found with leg data then must be manual scoring

    If (LegDataCount > 0) and (getarraylength(Pilot.Leg) = 0) and (Pilot.Dis > 0) and (round(Pilot.takeoff) <> 1) then
      begin
        Pilot.Warning := 'Glider ' + Pilot.CompID + ' has takefoff and distance set, if manually scoring set takeoff to 00:00:01 for correct windicapping';
        showmessage(Pilot.Warning);
      end;


end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// use values derived from pilot's flight trace or Pilot.Tag string to calculate pilots distance for each leg and 
// store total handicap distance in Pilot.td1, windicapped speed will go in Pilot.td2. 
procedure CalcPilotVariables(var Pilot : TPilot);

var
    x, xlegs: integer;
    sum_of_legs, leg_Hi, Leg_Hl, leg_distance, windicapped_leg : double;

begin

    Pilot.td1 := 0;
    Pilot.td3 := 0;
    sum_of_legs := 0;


    If debug_mode and Assigned_Area then Pilot.user_str1 := 'Hi values = ';

    If Pilot.Start <> -1 then Ns := Ns + 1;                               // count number of starts
    If (not Pilot.IsHC) and (Pilot.Start <> -1) then 
                              NsComp := NsComp + 1;                       // count number of competing starts

    xlegs := getarraylength(Pilot.Leg);                                   // get number of legs from trace

    for x := 1 to xlegs do                                                // for all task types calculate total marking distance on each leg
      begin
        If Fixed_Course then leg_hi := Task.Point[x].td1;                 // Hi already calculated for Fixed Course and held in Task record

        If Assigned_Area then leg_hi := CalcLegHi(Pilot.Leg[x-1].crs);    // Calculate Leg Hi for this leg using course from trace

        If debug_mode and Assigned_Area then
                Pilot.user_str1 := Pilot.user_str1 + formatfloat('0.000',leg_hi) + '  ';                  // for AAT debugging

        If not Distance_Handicapped then Leg_Hl := CalcLegHl(Pilot.Hcap,leg_hi) else Leg_Hl := 100;       // calculate Leg Hl

        If not Manual_Scoring and (xlegs > 0) then
                               leg_distance := Pilot.Leg[x-1].d;            // Get leg distance from trace

        windicapped_leg := (leg_distance * 100) / Leg_Hl;                   // full leg length windicapped

        sum_of_legs := sum_of_legs + leg_distance;                          // add actual leg length to sum_of_legs
        Pilot.td1 := Pilot.td1 + windicapped_leg;                           // add leg marking distance to total marking distance in Pilot.td1

      end;

    If Manual_Scoring and (Pilot.sdis > 0) then Pilot.td1 := Pilot.sdis;    // use distance given
    If Manual_Scoring and (Pilot.sdis = 0) and (Pilot.finish > 0) then
                        Pilot.td1 := CalculateWindicapTaskDist(Pilot.Hcap); // if no distance given the use windicapped task distance


    If Distance_Handicapped then Pilot.td1 := Pilot.td1 * DHT_hc_factor;    // adjust actual distance reported by SeeYou to get a representative handicapped distance
                                                                            // for finishers this should be equal to Handicapped Task Distance

    CalculatePilotHandicapSpeed(Pilot);                                     // Use result in Pilot.td1 to calculate pilot's windicap speed and store in Pilot.td2

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate Pilot handicapped speed and store in Pilot.Td2 (note result in m/s)
procedure CalculatePilotHandicapSpeed(var Pilot : TPilot);

var
    speed, startpenalty, starttime, TimeOnTask : double;
    timestring : string;

begin

    startpenalty := 0;
    starttime := round(Pilot.Start);                           // set starttime to Pilot.Start as default, rounded to avoid fp problems
    If RegattaStart and (round(Pilot.Start) >= StartOpen) then
      starttime := StartOpen;                                  // if Regatta then use StartOpen as pilot start unless pilot started early
                                                               // and calculate penalty for early start to add to TimeOnTask

    // Due to odd behaviour observed sometimes in SeeYou's handling of times, round StartOpen and Pilot.Start to ensure result is even number
    if (RegattaStart or HeldStart) and (round(Pilot.Start) < StartOpen) then startpenalty := 2*(round(StartOpen) - round(Pilot.Start));

    If Pilot.Finish <> -1 then                                 // check if pilot finished
      begin
        TimeOnTask := Pilot.Finish - starttime + startpenalty;
                                                         // for Fixed Course Tasks, Task.TaskTime will be 0,
                                                         // for AATs if Task.TaskTime > TimeOnTask then use Task.TaskTime

        If Task.TaskTime > TimeOnTask then TimeOnTask := Task.TaskTime;
        speed := 0;
        If TimeOnTask > 0 then speed := Pilot.Td1 / TimeOnTask;  // pilot.Td1 holds total windicapped distance flown, timeontask should be non-zero
                                                                 // but could be 0 if manual scoring and finish time entered incorrectly,
                                                                 // so don't let div by 0 fault crash script
      end
    else speed := -1;                                    // if not finished then set speed to -1 as a flag

    timestring := SecondsToTime(startpenalty);
    If (Pilot.Finish <> -1) and (startpenalty > 0) then Pilot.Warning := Pilot.Warning + 'Early start penalty added = '  + timestring + ' ';

    Pilot.Td2 := speed;                                  //store result in the Pilot.Td2 user variable

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// function to check if pilot is competing
function PilotCompeting (Pilot : TPilot) : boolean;

begin
    result := true;

    If Pilot.isHC      then result := false;             //check if Hors Concours

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// find Dmax, the greatest marking distance flown by a participating glider
function GreatestDistanceAnyGlider : double;
var
  x  : integer;
  marking_distance : double;

begin
    Result := 0;

    If Nl > 0 then                  // if gliders have launched find greatest Dm (in Pilot.Td1)
      begin
        for x := low(Pilots) to high(Pilots) do
          If ((Pilots[x].Td1 > result) and PilotCompeting(Pilots[x])) then
          Result := Pilots[x].Td1;
      end;

    // Dmax will be set by Result, if no finishers then set Dw too.

    If Nf < 1 then Dw := Result;

 
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// find DmaxLand, the greatest marking distance for a participating glider that landed out
function GreatestLandoutDistance : double;
var
  x  : integer;
  marking_distance : double;

begin
    Result := 0;

    If Nl > 0 then                  // if gliders have launched find greatest landout distance (in Pilot.Td1)
      begin
        for x := low(Pilots) to high(Pilots) do
          If ( (Pilots[x].Finish < 0) and (Pilots[x].Td1 > result) and PilotCompeting(Pilots[x]) ) then
          Result := Pilots[x].Td1;
      end;

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate the number of participating gliders finishing, Nf
function GetNumberFinishers : integer;
var
  x : integer;


begin
    Result := 0;
    for x := low(Pilots) to high(Pilots) do
    begin
      If ((Pilots[x].Finish > -1) and PilotCompeting(Pilots[x])) then
          Result := Result + 1;
    end;

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate Y distance
function CalculateY : double;
begin
    If Fixed_Course then begin
      If Nationals_Y then
        Result := WindiCapTaskDist * (Qualifying_distance_factor / 100)    // Nationals use %age of Windicapped Task Distance   (table 36.5)
      else
        Result := DTask * (Qualifying_distance_factor / 100)               // Regionals use %age of Unhandicapped Task Distance (table 36.5)
      end
    else
     //  AAT: Y is Qualifying_Time_Factor multiplied by number of hours set for task (divide Task.TaskTime by 3600 to get hours)

      Result := Qualifying_Time_Factor * (Task.TaskTime / 3600) * 1000;    // Multiply result by 1000 to get metres

    If result < YMin then result := YMin;                                  // Apply Min/Max (table 36.5)
    If result > YMax then result := YMax;
end;


//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate NY, the number of pilots passing Y
function CalculateNY : integer;
var
  x : integer;
begin
    result := 0;
    for x := low(Pilots) to high(Pilots) do
    begin
      If ((Pilots[x].Td1 > Y) and PilotCompeting(Pilots[x])) then  // Pilots[x].Td1 hold pilot's marking distance (Dm)
        Result := Result + 1;
    end;
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate Vh, the fastest handicapped speed achieved by a participating glider
function GreatestSpeed : double;

var
  x  : integer;
  finisher : boolean;

begin
    Vh := 0;
    Winners_Tg := 0;
    If Nf > 0 then
    begin
      for x := low(Pilots) to high(Pilots) do               // Pilot.Td2 holds Sh, finisher's speed or -1 for non-finishers
        If ((Pilots[x].Td2 > Vh) and PilotCompeting(Pilots[x]) and (Pilots[x].Finish <> -1)) then
          begin
            Vh         := Pilots[x].Td2;                    // for Fixed Course Task we have also found winner (not always true for AAT)
            Winners_Tg := Pilots[x].Finish-Pilots[x].Start; // Save Fixed Course Task winner's time (not used in AAT)
            Dw         := Pilots[x].Td1;                    // Save Fixed Course Task winner's marking distance (not used in AAT)
          end;
     end;
     Result := Vh;                                          // Return Vh as result
end;



//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate Nv, the number of participating gliders exceeding 2/3 winners speed
function CalculateNv : integer;
var
  x : integer;
begin
    result := 0;
    If Nf > 0 then
      begin
        for x := low(Pilots) to high(Pilots) do
          If ((Pilots[x].Td2 > (Vh*0.6667)) and PilotCompeting(Pilots[x])) then
            Result := Result + 1;
    end;
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate Day Factor Ff
function CalculateDayFactorFf : double;
begin
    Result := minvalue(1.0,1.0,(1.25 * (Ny / N)));  // result must not exceed 1.0
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate total day points
function CalculateDayPoints : double;
var
  FMaxDist, FMaxSpeed, FPilotsPastY,DayPoints : double;
  D, T : double;
  timestr, minstr : string;

begin
    Result := 0;

    If Assigned_Area then 
      D := Dmax / 1000 else                                         // Set D to greatest distance flown (in km) for AATs
      D := Dw / 1000;                                               // Set D to winner's distance (in km) for all other task types

    If Assigned_Area then 
      T := Task.TaskTime / 3600                                     // set T to task time (in hours) for AATs
    else T := Winners_Tg / 3600;                                    // else set T to winner's time (in hours) for all other task types

    FPilotsPastY := Ff * 1000;                                      // Day Factor determined by number past Y

    FMaxDist := Ff * ((5 * D) - Da);                                // Distance devaluation factor

    If Nf < 1 then                                                  // if no finishers
      FMaxSpeed := 9999                                             // set FMaxSpeed to obviously too high value
    else
      FMaxSpeed := Ff * ((400 * T) - Ta);                           // Speed devaluation factor


    DayPoints := minvalue(FMaxDist,FMaxSpeed,FPilotsPastY);         // the minimum of the above calculated values

    If Fixed_Course and (DTask < MinTaskDistance) then
      DayPoints := 0;                                               // Check task setter has not underset Fixed Course Task

    If Assigned_Area and (Task.TaskTime < MinTaskTime) then
      DayPoints := 0;                                               // Check task setter has not underset AAT

    timestr                       := 'Winners Time';
    If Assigned_Area then timestr := 'Task Time';
    minstr                        := 'Less than Minimum Task Distance';
    If Assigned_Area then minstr  := 'Less than Minimum Task Time';

   //set string Devalued to reflect what caused the day to be devalued
    If DayPoints = FMaxDist     then Devalued := 'Distance';
    If DayPoints = FMaxSpeed    then Devalued :=  timestr;
    If DayPoints = FPilotsPastY then Devalued := 'Number Past Y';
    If DayPoints = 0            then Devalued :=  minstr;
    If Ny = 0                   then Devalued := 'No  gliders past Y';

   // setup info3 to dump key parameters to info3 string to help debugging (gets overwritten in main procedure if debug_mode is false)
    info3 := 'N=' + inttostr(N);
    If Nw > 0 then info3 := info3 + '(withdrawn=' + inttostr(Nw) + ')';
    info3 := info3 + ', Nl=' + inttostr (Nl) + ',  Ny=' + inttostr(ny)+ ',  Nf=' + inttostr(nf) + ',  Nv=' + inttostr(Nv);
    info3 := info3 + ', D=' + FormatFloat('0.000',D) + ', T=' + FormatFloat('0.000',T)+ ',  F=Min(FF*1000=';
    info3 := info3 + FormatFloat('0.000',Ff*1000)+ ', Fdist=' + FormatFloat('0.000',FMaxDist) ;
    If FMaxSpeed < 9999.0 then info3 := info3 + ', FSpeed=' + FormatFloat('0.000',FMaxspeed);
    info3 := info3 + ')';

    Result := DayPoints;  // Return result (F)
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate day speed points Fv
function CalculateDaySpeedPointsFv : double;

begin
    Result := 0.6667 * F * (Nv / Nl);
    info3 := info3 + ',  Fv=' + FormatFloat('0.000',result);
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate day distance points
function CalculateDayDistancePoints : double;
begin
    Result := F - Fv;
    info3 := info3 + ',  Fd=' + FormatFloat('0.000',result);
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate Day points for each pilot (1000 point system)
procedure CalcPilotPoints(var Pilot : TPilot);
var
  Ps,Pd, Dm, Sh, ratio_dmax : double;
  exceeded_2thirds_Dmax,   finisher : Boolean;      // used below to try to aid readability!

begin
    If Pilot.Start = -1 then                                // check if pilot started 
    begin
      Pilot.Points := 0 - Pilot.penalty;                    // if not give 0 points (less any penalty) and exit procedure
      exit;
    end;

    Dm := Pilot.Td1;                                        // Pilot.Td1 holds pilot's marking distance
    Sh := Pilot.Td2;                                        // Pilot.Td2 holds the pilots marking speed

    exceeded_2thirds_Dmax := false;
    finisher := false; 

    If Pilot.Finish <> -1 then finisher := true; 

    If Dm > (0.6667*Dmax) then exceeded_2thirds_Dmax := true; // only used in AAT
    
// speed points
    Ps := 0;                                                // speed points are given in proportion to amount that Sh exceeds two-thirds Vh
    If Vh > 0 then Ps := 3 * Fv *((Sh / Vh) - 0.6667);      // calculate for all finishers
    If Ps < 0 then Ps := 0;                                 // if result negative then give zero speed points


// distance points
    ratio_dmax := 0;                                        // set ratio to zero
    If Dmax > 0 then                                        // avoid div by 0 
    begin
      If finisher and (Fixed_Course or exceeded_2thirds_Dmax) then     // Finisher on Fixed Course Task or AAT finisher with Dm > 2/3 Dmax,
                           ratio_dmax := 1;                            //   so set ratio to 1 to get full distance points

      If finisher and Assigned_Area and NOT exceeded_2thirds_Dmax then // AAT finisher with Dm <= 2/3 * Dmax
                           ratio_dmax := Dm / (Dmax * 0.6667);         //   so set ratio to Dm : 2/3Dmax

      If NOT finisher then ratio_dmax :=  (Dm/Dmax);                   // non-finisher, set ratio to Dm : Dmax

    end;    

    If ratio_dmax > 1 then Pd := Fd
      else Pd := Fd * ratio_dmax;                          // Glider Distance Points are Day Distance points multiplied by appropriate ratio

    Pilot.points := round(Pd + Ps - Pilot.penalty);        // Total points are the summed THEN rounded
end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate speed ranking for each finisher (Simplified Scoring)
procedure RankFinisher(var Pilot : TPilot);
var
  rank,x : integer;


begin

    If Pilot.Finish > 0 then                              // check if pilot started 
    begin
      rank := 1;                                          // so fastest gets rank 1

      for x := low(Pilots) to high(Pilots) do             // check all competing pilots for faster speeds
      begin
        If PilotCompeting(Pilots[x]) and (Pilots[x].Finish > 0) and (Pilots[x].Td2  > Pilot.Td2) then
          rank := rank + 1;                               // if faster finishers speed found then increment rank
      end;
    end;

    Pilot.Td3 := 0;
    If PilotCompeting(Pilots[x]) then Pilot.Td3 := rank;     // Set rank for this pilot

end;
//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate landout ranking for each pilot (Simplified Scoring)
procedure RankLandout(var Pilot : TPilot);
var

  rank,x,slot : integer;
  dland : double;

begin
    If Pilot.Finish > 0 then exit;                          // exit if pilot finished, already ranked

    dland := Pilot.Td1;                                     // save marking distance for landout

    If DSlotLength > 0 then
      slot := trunc((DmaxLand - dland)/DSlotLength);       // calculate difference between pilot's marking distance from greatest landout's
                                                           // and divide by DSlotLength then truncate to get slot number (number of slots behind best landout)

    rank := 1;                                              // Furthest landout will be ranked top of landouts

    for x := low(Pilots) to high(Pilots) do                 // check all non-finishing pilots and count how many have smaller slot number or distance 
      begin
        If (DSlotLength > 0) and PilotCompeting(Pilots[x]) and (Pilots[x].Finish < 0) and (slot > trunc((DmaxLand - Pilots[x].Td1)/10000)) then
           rank := rank + 1;                                // increment rank for each smaller slot
        If (DSlotLength = 0) and PilotCompeting(Pilots[x]) and (Pilots[x].Finish < 0) and (dland < Pilots[x].Td1) then
           rank := rank + 1;                                // increment rank for each greater landout
      end;

    Pilot.Td3 := 0;
    If PilotCompeting(Pilots[x]) then Pilot.Td3 := Nf + rank; // Add number of finishers to set rank for this pilot 

end;

//------------------------------------------------------------------------------------------------------------------------------------------------
// calculate points for each pilot (Simplified Rules)
procedure CalcPilotSimplePoints(var Pilot : TPilot);

begin

    If Pilot.isHC then                                      // if not competing give 0 points (ignore any penalty)
    begin
      Pilot.Points := 0;                                    // if not give 0 points (less any penalty) and exit procedure
      exit;
    end;

    If (Nf < 1) or (Pilot.Start = -1) then                  // check if we have finishers and pilot started 
    begin
      Pilot.Points := 0 - Pilot.penalty;                    // if not give 0 points (less any penalty) and exit procedure
      exit;
    end;

    Pilot.points := NsComp + 1 - Pilot.Td3 - Pilot.penalty; // Points = Number of starters + 1 - rank - penalty

    If Pilot.Td3 = 1 then Pilot.points := Pilot.points + 1; // Give bonus for first place

    If Pilot.points < 0 then Pilot.points := 0;             // Don't allow negative points

end;
//------------------------------------------------------------------------------------------------------------------------------------------------
// display the result for each pilot for publication.
procedure DisplayResults;
var
  x1 : integer;

begin
    For x1 := 0 to GetArrayLength(Pilots) - 1 do
    begin

      // output debug info in warning if required
      If (length(Pilots[x1].User_Str2) > 0) or (length(Pilots[x1].User_Str3) > 0) then 
          Pilots[x1].Warning := Pilots[x1].CompId + Pilots[x1].User_Str2 + Pilots[x1].User_Str3;

      // for team entries with different handicaps confirm handicap used
      if uppercase(Pilots[x1].PilotTag) = 'TEAM' then
        Pilots[x1].Warning := Pilots[x1].Warning + Pilots[x1].CompId + ': scored with handicap = ' + FormatFloat('0.0',Pilots[x1].Hcap) +' ';

      Pilots[x1].Sstart  := Round(Pilots[x1].Start);  // scored start is start time from SeeYou (rounded because of SeeYou fp problems)
      If RegattaStart and (Round(Pilots[x1].Start) >= StartOpen) then 
        Pilots[x1].Sstart  := StartOpen;              // display start gate open time as start time (still use Pilot.Start for Held starts)

      Pilots[x1].Sfinish := Pilots[x1].Finish;        // scored finish is finish time from SeeYou

      If (RegattaStart or HeldStart) and (Pilots[x1].Finish <> -1) and (Pilots[x1].Start < StartOpen) then 
        Pilots[x1].Sfinish  := Pilots[x1].Finish + 2*(round(StartOpen) - round(Pilots[x1].Start)) ;    // add penalty to finish time

      Pilots[x1].Sspeed := 0;
      If (Pilots[x1].Finish > 0) and (Pilots[x1].Td2 > 0) then Pilots[x1].Sspeed := Pilots[x1].Td2;

// td1 holds scored distance in metres, dis holds actual flown distance in metres. Truncate both to integer metres,
// then add dis/10000000 to tdis, so that sdis will hold scored distance to left of decimal place and actual distance to right.
// Rounding sdis will still display correct scored distance to nearest 10 metres (0.01 km) and actual flown distance is now available from Soaring Spot API

      Pilots[x1].Sdis := Trunc(Pilots[x1].Td1) + (Trunc(Pilots[x1].Dis)/10000000) ;           // display windicapped distance held in td1

      If Distance_Handicapped  and (not ThousandPoint) and (Pilots[x1].Finish = -1) then 
                Pilots[x1].Sdis := Pilots[x1].Dis ;                                           // For Simplified Rules display actual landout distance given by SeeYou

    end;

end;



//Main Program starts here-----------------------------------------------------------------------------------------------------

begin

    Info1 := '';                                                        // Initialize Info strings as SeeYou 10.60 does not set these up as null
    Info2 := '';
    Info3 := '';
    Info4 := '';

    If CompClass = '?' then begin                                       // Check to see if CompClass has been set
      Info4 := 'No Comp Class selected in Scoring script !!';
      exit;
    end;

    SetUpCompParameters;                                                // Sets the factors that vary according to Contest and task type

    If handicapped_comp then begin                                      // Check for invalid speed indices (for DHTs, handicapped_comp will still be true at this point)
      If CheckHcaps then begin                                          // CheckHcaps function will put message out using Showmessage detailing errors found
        Info4 := 'Glider found with invalid handicap !!?';              // put info on scorer's screen
        exit;
      end;
    end;

    Nl := GetNumberPilotsLaunched;                                      // How many launched?

    If Nl < 1 then begin                                                // if none, then exit
      Info4 := 'No Glider Launches Detected !!';
      exit;
    end;

    Ns := 0;                                                            // zero number of starts
    NsComp := 0;                                                        // zero number of competing starts

    GetTaskInfo;                                                        // Get task info and set up flags and arrays for leg data
    ParseDayTag(DayTag);                                                // Parse the DayTag string for additional task info, contest wind etc. and set booleans

    N  := GetNumberGliders;                                             // Get number of gliders excluding Hors Concours and withdrawn

    If not Assigned_Area then SetDtask;                                 // Set Dtask (task distance)

    If Fixed_Course then CalculateTaskHiValues;                         // get Hi value for each leg of Fixed Course Task

    If Fixed_Course and Nationals_Y then WindicapTaskDist := CalculateWindicapTaskDist(Hbase);  // get windicap task distance for use in determining Y in Nationals

    for i := low(Pilots) to high(Pilots) do                             // calculate windicap distance and speed for each pilot
      begin

        CheckPilotData(Pilots[i]);                                      // check to see if trace data present or manually scored

        If Pilots[i].Takeoff > 0 then                                   // If launch detected
          CalcPilotVariables(Pilots[i]);                                // calculate scoring distance and speed
      end;  


    If (Nl - ManualCount > 0) and (LegDataCount = 0) and (Ns > 0) then begin
                                                                        // if we have data derived from traces, starts found, but no trace leg data
      Info4 := 'Please ensure that "Need task legs data in scoring script" is ticked in Contest Properties/Options';
      exit;
    end;

   //calculate scoring variables

    Nf := GetNumberFinishers;                                           // How many competing gliders finished?

    Dmax := GreatestDistanceAnyGlider;                                  // also sets Dw if no finishers
    DmaxLand := GreatestLandoutDistance;                                // find greateset landout distance (for Simplified Rules)


    If ThousandPoint then begin
       Y  := CalculateY;                                                // calculate the qualifying distance, Y
       NY := CalculateNY;                                               // and find out how many exceeded it
    end;
    Vh := GreatestSpeed;                                                // calculate greatest speed (and for Fixed Course set Winners_Tg and Dw)
    Nv := CalculateNv;                                                  // how many finishers exceeded 2/3 Vh
    If ThousandPoint then begin
       Ff := CalculateDayFactorFf;                                      // calculate day factor
       F  := CalculateDayPoints;                                        // calculate total day points
       Fv := CalculateDaySpeedPointsFv ;                                // calculate spilt of day points into speed points
       Fd := CalculateDayDistancePoints;                                // and distance points
    end;


    for i := low(Pilots) to high(Pilots) do                             // calculate points for each pilot
      begin
        If ThousandPoint then CalcPilotPoints(Pilots[i]) else           // calculate using 1000 point system
        begin                                                           // otherwise
          RankFinisher(Pilots[i]);                                      // rank by speed
          RankLandout(Pilots[i]);                                       // rank by distance
          CalcPilotSimplePoints(Pilots[i]);                             // calculate points
        end;
      end;

   DisplayResults;                                                      // Loads fields in Pilot records for display (start, Finish, Speed, Distance)

   // information about task is displayed using strings Info1, Info2, Info3 and Info4
   // Info1 is set in the ParseDayTag procedure.
    Info2 := '';
    If ThousandPoint then Info2 := 'Y distance = ' + FormatFloat('0.0',Y / 1000) + ' km';
    If ThousandPoint and Distance_Handicapped then Info2 := Info2 + ',  ';

    If Distance_Handicapped          then Info2 := Info2 + 'Handicapped Task distance = '+ FormatFloat('0.0',Hcap_task_dist) + ' km';
    If Distance_Handicapped and (Hcap_task_dist = 0)
                                     then Info2 := Info2 + ' (DTask=' + FormatFloat('0.0',DTask/1000) + ' km)';
    If Fixed_Course   and debug_mode then Info2 := Info2 + ',  ' + Hi_string;

    If not ThousandPoint then
      begin
        Info3 := '';
        If RegattaStart then Info3 := 'Regatta Start at ' + RStartString + ',  ';
        If HeldStart then Info3 := 'Held Start at ' + HStartString + ',  ';
        Info3 := Info3 + 'Number of starters = ' + Inttostr(round(NsComp)) + ',  Number of finishers = ' + Inttostr(round(Nf));
        If Nf = 0 then Info4 := Info4 + 'No Contest Day  (no pilot completed task)';
      end;

    If ThousandPoint and not debug_mode then Info3 := 'Day Speed Points = ' + Inttostr(round(Fd+Fv)-round(Fd)) +
                                                   ',  Day Distance Points = ' + Inttostr(round(Fd));
    
    If ThousandPoint and (F < 1000) then Info4 := Info4 + 'Reason for Devaluation : ' + Devalued;
   

end.
